2  “DNA Libraries, Duplex Sequencing, and NanoSeq — A Practical Summary”

Author

“Prepared for Bick Lab / EOR”

Published

Invalid Date

2.1 Key takeaways

  • Standard library prep (fragment → end-repair/A-tail → adaptor ligation → PCR) enables scalable sequencing but introduces strand-asymmetric errors.
  • Combinatorial dual indices (CDI) are convenient but vulnerable to index hopping; unique dual indices (UDI) mitigate this.
  • DS collapses PCR duplicates by UMI-labeled families on each strand and then across strands; residual artifacts often arise when single-stranded damage is copied during end-repair.
  • NanoSeq minimizes those artifacts by forgoing conventional end-repair (using enzymatic blunt-ending and ddBTPs), achieving ~5×10^-9 per-base error in practice.

2.1.1 Library construction — why each step exists

1.  Fragmentation (mechanical or enzymatic) → keep inserts within high-quality read lengths (~200–500 bp) because read quality decays after ~300 cycles.
2.  End-repair & A-tailing → produce blunt, 5′-phosphorylated, 3′-adenylated ends so one ligation chemistry works for diverse fragments.
3.  Adaptor ligation → introduces common priming sites (P5/P7, Rd1/Rd2 SP) and sample indices.
4.  Library amplification → boosts low-input material and completes full-length primer/flow-cell motifs.
5.  Sequencing → pooled, indexed libraries bind the flow cell (P5/P7), enabling multiplexing.

2.1.2 Indexing strategies and index hopping

CDI enables large multiplexing but exposes samples to misassignment if indices hop. UDI reduces this risk.

Back-of-the-envelope effect of index hopping

If sample A1 has a true variant at 50% VAF and A2 is wild-type (0% VAF), and 1% of A1 reads hop into A2, the apparent VAF in A2 is ~0.5% — potentially above rare-variant thresholds.

true_vaf_A1 <- 0.50
hop_rate <- 0.01
apparent_vaf_A2 <- true_vaf_A1 * hop_rate
apparent_vaf_A2
#> [1] 0.005

Implication: For ultra-rare calls (≤0.5%), UDI and stringent demultiplexing are essential.

2.1.3 Where errors come from (and why many are strand-asymmetric)

•   Pre-ligation damage: deamination/depurination/mismatches on one strand.
•   End-repair fill-in: polymerase misincorporation, slippage, template switching.
•   PCR & sequencing: base substitution/indel/phasing.
•   Ligation artifacts: misligation, concatemers.

Most events hit one strand; seeing the same complementary change on both strands at the same position is improbably rare unless a true variant is present.

2.1.4 Duplex sequencing (DS)

UMI-labeled adaptors mark original molecules and strand of origin (Read1 UMI = A, Read2 UMI = B → top vs. bottom). Workflow: 1. Build single-strand consensus sequences (SSCS) by collapsing UMI families on each strand. 2. Build a duplex consensus sequence (DCS) by comparing SSCS pairs from complementary strands.

Minority errors are filtered within SSCS; majority errors that dominated one strand are filtered when forming the DCS — provided the complementary strand is unaffected.

Expected error scale if errors are independent

Let per-base error probability on each strand be p. Requiring concordant complementary changes implies error ≈ p^2. If typical polymerase+sequencer error is ~10^-3 to 10^-4, a naïve expectation for DCS error is 10^-6 to 10^-8. Empirically, many DS implementations report ≈10^-7 due to non-independent errors (e.g., single-strand damage copied during end-repair).

p <- c(1e-3, 3e-4, 1e-4)
 tibble(p = p, expected_DCS_error = p^2)
#> # A tibble: 3 × 2
#>        p expected_DCS_error
#>    <dbl>              <dbl>
#> 1 0.001          0.000001  
#> 2 0.0003         0.00000009
#> 3 0.0001         0.00000001

2.1.5 Why DS can plateau around ~1e-7

If a damaged base on one strand is converted to a stable change on both strands during end-repair/fill-in, DCS will accept it — it mimics a true variant. Thus, reducing or eliminating such conversions is pivotal.

2.1.6 NanoSeq: the key innovation

NanoSeq uses enzymatic blunt-ending and dideoxy-nucleotides (ddBTPs) to arrest end-repair, minimizing the copying of single-strand lesions into duplex artifacts. With UDI + UMI + DCS, the overall error rate drops toward ≤5×10^-9 in practice when sufficient coverage and stringent filters are applied.

2.1.7 Coverage math and scalability

Below, we reproduce the deck’s throughput estimates for a 40 kb mouse CHIP panel and the coverage required to detect variants appearing once at duplex level.

Assumptions (adjust as needed): • Duplex coverage target ≈ 665× (sufficient to see a one-time DCS event with high confidence) • Raw (pre-consensus) coverage per bp ≈ 21,000× • Read length chemistry: 2×150 (≈300 cycles per paired read) • Capture efficiency ≈ 70% • NovaSeq 25B yields: specify reads per flow cell (e.g., 15–20 billion)

panel_bp            <- 4e4        # 40 kb
raw_cov_per_bp      <- 2.1e4      # 21,000× raw coverage per bp
cycles_per_PE_read  <- 300        # 2x150
capture_efficiency  <- 0.70       # 70%

cycles_per_sample   <- panel_bp * raw_cov_per_bp
reads_per_sample    <- cycles_per_sample / cycles_per_PE_read
reads_per_sample_adj<- reads_per_sample / capture_efficiency

# Illustrate with two plausible flow-cell yields
flowcell_reads_low  <- 1.5e10
flowcell_reads_high <- 2.0e10
samples_low_yield   <- floor(flowcell_reads_low  / reads_per_sample_adj)
samples_high_yield  <- floor(flowcell_reads_high / reads_per_sample_adj)

tribble(
  ~quantity,                ~value,
  "Cycles per sample",       scales::comma(cycles_per_sample),
  "Reads per sample (raw)",  scales::comma(reads_per_sample),
  "Reads per sample (70%)",  scales::comma(reads_per_sample_adj),
  "Samples per 25B (15B)",   scales::comma(samples_low_yield),
  "Samples per 25B (20B)",   scales::comma(samples_high_yield)
) %>% knitr::kable()
quantity value
Cycles per sample 840,000,000
Reads per sample (raw) 2,800,000
Reads per sample (70%) 4,000,000
Samples per 25B (15B) 3,749
Samples per 25B (20B) 4,999

Rule-of-thumb costs (example): if library prep ≈ $32/sample and a 25B flow cell ≈ $20,000, sequencing cost per sample ≈ $3 at the high-multiplex end, for a total of ≈ $35 per sample (panel-scale). Adjust for your actual yields and kit pricing.

2.1.8 Simple simulation: how consensus reduces errors

This stylized simulation generates two independent read families (top/bottom) with per-base error p. We form SSCS by majority vote and DCS by agreement between strands.

set.seed(1)
sim_consensus_error <- function(n_sites = 1e5, fam_size = 6, p = 1e-3){
  # top & bottom strand reads
  top  <- matrix(rbinom(n_sites * fam_size, 1, p),  nrow = n_sites)
  bot  <- matrix(rbinom(n_sites * fam_size, 1, p),  nrow = n_sites)
  # SSCS: majority vote within each family
  sscs_top <- rowMeans(top) > 0.5
  sscs_bot <- rowMeans(bot) > 0.5
  # DCS: require concordance between SSCS
  dcs_call <- sscs_top & sscs_bot
  c(
    raw_error      = mean(top[,1] == 1),
    sscs_error_top = mean(sscs_top),
    dcs_error      = mean(dcs_call)
  )
}

# Explore a grid of p and family sizes
grid <- expand.grid(p = c(1e-3, 3e-4, 1e-4), fam = c(3,6,10))
res <- purrr::pmap_dfr(grid, ~{
  out <- sim_consensus_error(n_sites = 5e4, fam_size = ..2, p = ..1)
  tibble(p = ..1, fam = ..2, raw = out[1], sscs = out[2], dcs = out[3])
})
res %>% mutate(across(c(raw, sscs, dcs), signif, 3)) %>% knitr::kable()
#> Warning: There was 1 warning in `mutate()`.
#> ℹ In argument: `across(c(raw, sscs, dcs), signif, 3)`.
#> Caused by warning:
#> ! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
#> Supply arguments directly to `.fns` through an anonymous function instead.
#> 
#>   # Previously
#>   across(a:b, mean, na.rm = TRUE)
#> 
#>   # Now
#>   across(a:b, \(x) mean(x, na.rm = TRUE))
p fam raw sscs dcs
1e-03 3 0.00080 0 0
3e-04 3 0.00030 0 0
1e-04 3 0.00012 0 0
1e-03 6 0.00078 0 0
3e-04 6 0.00028 0 0
1e-04 6 0.00004 0 0
1e-03 10 0.00106 0 0
3e-04 10 0.00044 0 0
1e-04 10 0.00010 0 0

Interpretation: SSCS reduces errors roughly with family size; DCS produces ≈p^2 scaling when strand errors are independent. Deviations in real data usually signal correlated/pre-repair artifacts.

2.1.9 Method comparison (at a glance)

tribble(
  ~Method, ~Core_idea, ~Typical_error_rate, ~Strengths, ~Considerations,
  "Standard (CDI)",  "Indexed library; no duplex collapsing", "~1e-3 to 1e-4", "Easy, cheap, scalable", "Index hopping; PCR/seq errors dominate",
  "DS + UMI (UDI recommended)", "SSCS → DCS across complementary strands", "~1e-7 (empirical)", "Suppresses most PCR/seq errors", "Sensitive to end-repair–copied damage",
  "NanoSeq (DS+no end-repair)", "Blunt-ending + ddBTPs arrest; DCS", "≤5e-9 (practical)", "Ultra-rare variant detection; cleaner spectra", "Cost, throughput, chemistry constraints"
) %>% knitr::kable()
Method Core_idea Typical_error_rate Strengths Considerations
Standard (CDI) Indexed library; no duplex collapsing ~1e-3 to 1e-4 Easy, cheap, scalable Index hopping; PCR/seq errors dominate
DS + UMI (UDI recommended) SSCS → DCS across complementary strands ~1e-7 (empirical) Suppresses most PCR/seq errors Sensitive to end-repair–copied damage
NanoSeq (DS+no end-repair) Blunt-ending + ddBTPs arrest; DCS ≤5e-9 (practical) Ultra-rare variant detection; cleaner spectra Cost, throughput, chemistry constraints

2.1.10 Practical notes for your runs

•   Prefer UDI over CDI for high-plex panels.
•   Track UMI families and require minimum family sizes for SSCS.
•   Enforce duplex pairing heuristics (complementary SSCS only; orientation/insert compatibility).
•   Guard against chimeras/concatemers and soft-clipped alignments near ligation sites.
•   Use context-specific filters (e.g., strand concordance, read position bias, base quality, map quality, homopolymer runs).
  1. References (for orientation) • Abascal et al., 2021 — introduced NanoSeq approach and error characterization. • Lawson et al., 2024 — follow-up refinements/validations of NanoSeq-like chemistries. • Illumina application notes (UDI vs CDI); IDT guidance on index hopping.